#ifndef __YUECHUAN_IOMANAGER_H
#define __YUECHUAN_IOMANAGER_H

#include "scheduler.h"
#include "timer.h"
#include <vector>

namespace yuechuan {
    /**
     * @brief io协程调度器
     * @inherit Scheduler 协程调度器
     * @inherit TimerManager 时间管理类
     * @overload tickle idle stopping()这三个虚函数
     */
    class IOManager : public Scheduler, public TimerManager{
    public:
        typedef std::shared_ptr<IOManager> ptr;
        typedef RWMutex RWMutexType;

        enum Event {
            NONE = 0x0,
            READ = 0x1,
            WRITE = 0x4
        };
    private:
        /**
         * @brief 文件句柄对象
         */
        struct FdContext {
            typedef Mutex MutexType;
            /**
             * @brief 事件上下文对象
             * @details 事件可能为函数，也可能为一个协程，要指定绑定的调度器
             */
            struct EventContext {

                Scheduler *scheduler = nullptr;                // 事件执行调度器，一个事件可以有多个协程调度器
                Fiber::ptr fiber;                              //  事件协程
                std::function<void()> cb;                      //  事件回调函数
            };

            /**
             * @brief 获取事件绑定的事件上下文对象
             * @param event 需要获取上下文的事件
             * @return EventContext& 绑定的上下文事件对象的引用
             */
            EventContext &getContext(Event event);

            /**
             * @brief 重置事件
             * @param ctx 需要重置的事件上下文对象
             */
            void resetContext(EventContext &ctx);

            /**
             * @brief 唤醒事件
             * @details 首先要判断调用其的对象中保存的事件   是否包含   传入的要唤醒的事件
	         *          然后就获取当前事件绑定的事件上下文，将其加入到协程调度器中进行等待调度即可
             * @param event 要唤醒的事件
             */
            void triggerEvent(Event event);

            EventContext read;                                  /// 读事件
            EventContext write;                                 /// 写事件
            int fd = 0;                                         /// 事件句柄
            Event events = NONE;                                /// 已经注册的事件
            MutexType mutex;                                    /// 使用互斥锁即可

        };

    public:
        /**
         * @brief 构造函数
         * @param count 线程个数
         * @param use_caller 是否将该线程放到本调度器中
         * @param name 调度器名称
         */
        explicit IOManager(size_t count = 1, bool use_caller = true, const std::string &name = "UNKNOW");

        /**
         * @brief 析构函数
         */
        ~IOManager();

        /**
         * @brief 向管理器中添加事件
         * @details   新增加事件要先判断其句柄是否再事件队列中
                        * 若在直接将其赋值
                        * 否则要重新分配事件队列大小
                    * 检查句柄绑定的上下文中的事件是否和当前要添加事件相同
                        * 相同则说明错误添加相同的事件，抛出异常
                        * 不同才能考虑是修改还是添加
                    * 检查当前句柄对应的事件是否存在
                        * 存在则修改  EPOLL_CTL_MOD
                        * 不存在则添加 EPOLL_CTL_ADD
                    * 设置事件的执行是的协程调度器
                        * 当函数传递了一个函数时，表明该事件是一个函数，任务作为函数来执行
                        * 当函数传递的非函数时，事件要绑定协程为当前协程
         * @param fd 文件句柄
         * @param event 要添加的事件
         * @param func 回调函数
         * @return int  0表示成功，-1表示失败
         */
        int addEvent(int fd, Event event, std::function<void()> func = nullptr);

        /**
         * @brief 删除事件
         * @details   删除句柄要先判断其句柄是否再事件队列中
                        * 若不在直接返回false
                        * 若在才能对其进行操作
                    * 检查句柄绑定的上下文中的事件是否和当前要添加事件相同
                        * 不同直接返回false
                        * 相同才能对其进行操作
                    * 检查当前句柄对应的事件和传入的事件是否相同
                        * 相同则删除  EPOLL_CTL_DEL
                        * 不同则修改 EPOLL_CTL_MOD
                    * 删除了事件就要将该事件对应的上下文进行重置操作
         * @param fd 文件句柄
         * @param event 要删除的事件
         * @return bool 返回是否删除成功
         */
        bool delEvent(int fd, Event event);

        /**
         * @brief 取消事件，并强制触发
         * @details 强制执行对应的句柄事件
         *          要判断句柄是否在内，事件是否相同
         *          事件上下文中有针对强制唤醒事件的函数，活动对应的句柄事件的上下文然后调用即可
         * @param fd 文件句柄
         * @param event 要取消的事件
         * @return bool 返回是否取消成功
         */
        bool cancelEvent(int fd, Event event);

        /**
         * @brief 清除句柄上绑定的所有事件
         * @details 强制执行了所有的句柄事件
         *          和强制执行一个事件相似
         *          要针对read和write事件进行特殊处理
         * @param fd 文件句柄
         * @return bool 是否清空成功
         */
        bool cancelAll(int fd);

        /**
         * @brief 获取当前IO调度器指针
         * @details 事件调度器所在IOManger
         * @return IOManager*
         */
        static IOManager *GetThis();

    protected:
        /**
         * @brief IOManager是否停止
         * @details 需要知道等待事件的个数是否为0
         *          需要知道是否还有定时器在计时
         *          都满足时才返回true进行停止
         * @return 是否停止 上述条件满足返回true，否则返回false
         */
        bool stopping() override;

        /**
         * @brief 轮询
         * @details 重写父类的虚方法，用于没有任务调度时轮空
         *          IOManager中的轮空时根据epoll_wait来实现的
         *          他会检测事件的触发，当没有事件触发时会阻塞在epoll_wait处
         *          有事件触发时会进行tickle相应的任务
         *          如果是读事件就会从缓冲区读取一定的数据
         *          对于其他类型的事件就会获取对应ptr参数中的句柄上下文，其中绑定了对应的事件
         *          然后在将对应的事件进行trigger唤醒即可
         */
        void idle() override;

        /**
         * @brief 唤醒
         * @details 首先判断是否有空闲线程
         *              有则简单写入一个T字符
         *              没有就直接返回
         */
        void tickle() override;


        /**
         * @brief IOManager是否停止
         * @details 首先要满足父类的协程调度器的停止条件
         *          然后是IO待执行的任务队列为空
         *          无参调用下面的有参函数
         *
         * @return 是否停止
         */
        bool stopping(uint64_t &timeout);

        /**
         * @brief 重新分配句柄上下文数组大小
         * @details 对于每个上下文对应的句柄都设置为对应其队列中的下标
         *          方便后面对其回调函数的调用
         * @param size 指定的数组大小
         */
        void contextResize(size_t size);

        /**
         * @brief 对于在定时器队列最开始添加一个定时器要执行的函数
         * @inherit TimerManager
         * @details 对于IOManager的做法是唤醒epoll_wait来重新设置一下等待时间
         */
        void onTimerInsertForFront() override;

    private:
        int m_epfd;                                                // epoll句柄
        int m_tickleFds[2];                                        // 使用到pipe中的两端

        std::atomic<size_t> m_pendingEventCount = {0};            // 等待执行的事件数目
        RWMutexType m_mutex;
        std::vector<FdContext *> m_fdContexts = {nullptr};                    // 句柄上下文数组,数组下标就是文件句柄号

    };

} // namespace yuechuan




#endif